package socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 聊天室的服务端
 */
public class Server {
    private ServerSocket serverSocket;//创建一个客户端并监听端的连接
//线程池
    private ExecutorService threadPool = Executors.newFixedThreadPool(50);
//        private List<PrintWriter> allOut = new ArrayList<>();//该集合存放所有客户端的输出流
//     key:客户端的昵称     value:该客户端的输出流
    private Map<String, PrintWriter> allOut = new HashMap<>();

    public Server() {//构造方法 初始化服务端
        try {
            System.out.println("正在启动服务端......");
            serverSocket = new ServerSocket(8088);
            threadPool = Executors.newFixedThreadPool(40);
            System.out.println("服务端启动完毕");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        try {
            //不断等待客户端连接
            while (true) {
                System.out.println("等待客户端连接......");
                Socket socket = serverSocket.accept();//接受客户端的连接
                System.out.println("一个客户端已连接");
                //启动一个线程来处理与该客户端的交互
                ClientHandler handler = new ClientHandler(socket);
                threadPool.execute(handler);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }

    /**
     * 该线程任务负责与指定客户端交互
     */
    private class ClientHandler implements Runnable {
        private Socket socket;
        private String host;//记录该客户端的IP地址
        private String nickName;//记录客户端昵称

        public ClientHandler(Socket socket) {
            this.socket = socket;
            //通过socket获取远端（客户端）的IP地址
            host = socket.getInetAddress().getHostAddress();
        }

        public void run() {
            PrintWriter pw = null;
            try {
                //获取输入流，为读取客户端数据做准备
                InputStream in = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
                BufferedReader br = new BufferedReader(isr);
                //首先单独读取客户端发送过来的第一行字符串,它应当是客户端的昵称
                nickName = br.readLine();
                //通过socket获取输出流,用于将消息发送给该客户端
                OutputStream out = socket.getOutputStream();
                OutputStreamWriter osw = new OutputStreamWriter(out, StandardCharsets.UTF_8);
                BufferedWriter bw = new BufferedWriter(osw);
                pw = new PrintWriter(bw, true);

                /**
                 * 通常同步监视器对象可以指定为临界资源
                 * 原则:抢谁就锁谁
                 */
                synchronized (allOut) {
                    //将该输出流存入到共享集合allOut中
//                    allOut.add(pw);
                    int num = 1;
                    String newNickName = nickName;
                    while (true){
                        if (allOut.containsKey(nickName)){
                            nickName = nickName + num;
                            num++;
                        }else {
                            allOut.put(nickName, pw);
                            nickName = newNickName;
                            break;
                        }
                    }

                    allOut.put(nickName, pw);
                }
                //广播消息，告知所有客户端该用户上线了
                sendmessage(nickName + "[" + host + "]上线了,当前人数:" + allOut.size());
                //不断读取客户端发送的消息
                String message;
                while ((message = br.readLine()) != null) {
                    if (message.startsWith("@")) {
                        sendMessageToSomeOne(message);
                    } else {
                        //输出格式:昵称[ip]说：********
                        sendmessage(nickName + "[" + host + "]说:" + message);
                        saveMessage(nickName, null,message);
                    }
                    //将消息发送给该客户端
//                    System.out.println(nickName + "[" + host + "]:"+ message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //处理客户端断开连接后的操作
                //1将该客户端的输出流从allOut中移除
                synchronized (allOut) {
//                    allOut.remove(pw);
                    allOut.remove(nickName);
                }
                //2通知所有客户端该用户下线了
                sendmessage(nickName + "下线了,当前人数:" + allOut.size());
                //3关闭socket释放资源
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        /**
         * 将给定消息发送给所有客户端
         *
         * @param message
         */
        public void sendmessage(String message) {
            System.out.println(message);
            synchronized (allOut) {
//                for (PrintWriter o : allOut) {
//                    o.println(message);
//                }
                for (PrintWriter o : allOut.values()) {
                    o.println(message);
                }
            }
        }

        /**
         * 私聊功能
         *
         * @param message
         */

        private void sendMessageToSomeOne(String message) {
            /*
                私聊格式:
                @昵称:消息
                1:获取对方昵称
                2:根据昵称去allOut中获取对方的输出流
                3:将消息单独发送给对方
             */
            /*
                必要的格式验证
                @.+:.+
             */
            if (message.matches("@.+:.+")) {
                String toNickName = message.substring(1, message.indexOf(":"));
                if (allOut.containsKey(toNickName)) {
                    PrintWriter pw = allOut.get(toNickName);
                    message = message.substring(message.indexOf(":") + 1);
                    //3
                    String toMessage = nickName + "悄悄对你说:" + message;
                    pw.println(toMessage);
                    //4.将私聊信息保存到数据库
                    saveMessage(nickName,toNickName,message);
                } else {
                    PrintWriter pw = allOut.get(nickName);
                    pw.println(toNickName + "不在线");
                }
            } else {
                PrintWriter pw = allOut.get(nickName);
                pw.println("私聊格式不正确,正确格式:@对方昵称:内容");
            }
        }
        private void saveMessage(String fromUser,String toUser,String message){
            /*
            CREATE TABLE chatinfo(
          id INT PRIMARY KEY AUTO_INCREMENT,     主键
          content VARCHAR(255),                  聊天内容
          from_user VARCHAR(30),                 谁发的
          to_user VARCHAR(30)                    发给谁
      )
             */
            try (
                    Connection connection = DBUtil.getConnection();
            ){
                String sql = "INSERT INTO chatinfo(content,from_user,to_user) " +
                             "VALUES (?,?,?)";
                PreparedStatement ps = connection.prepareStatement(sql);
                ps.setString(1,fromUser);
                ps.setString(2,toUser);
                ps.setString(3,message);
                ps.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            }

        }
    }
}